package com.homedo.microservice.open.service.gateway.alert.task;


import com.homedo.common.util.Detect;
import com.homedo.microservice.open.service.gateway.alert.gateway.bean.enums.foralertnotice.StatusEnum4AlertNotice;
import com.homedo.microservice.open.service.gateway.alert.gateway.bean.enums.forrouteserviceurl.StatusEnum4RouteServiceUrl;
import com.homedo.microservice.open.service.gateway.alert.gateway.bean.po.*;
import com.homedo.microservice.open.service.gateway.alert.gateway.persistence.dao.AlertHistoryDao;
import com.homedo.microservice.open.service.gateway.alert.gateway.persistence.dao.AlertNoticeDao;
import com.homedo.microservice.open.service.gateway.alert.gateway.persistence.dao.AlertUserRouteServiceDao;
import com.homedo.microservice.open.service.gateway.alert.gateway.persistence.dao.AlertingDao;
import com.homedo.microservice.open.service.gateway.alert.gateway.service.HealthCheckingErrorStatisticsService;
import com.homedo.microservice.open.service.gateway.alert.gateway.service.RouteServiceUrlService;
import com.homedo.microservice.open.service.gateway.alert.support.CurAlertingSupportHolder;
import com.netflix.config.DynamicIntProperty;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.DynamicStringProperty;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;

/**
 * 需要告警的服务检测
 */
@Component
public class AlertDetectTask {

    @Autowired
    private RouteServiceUrlService routeServiceUrlService;
    @Autowired
    private HealthCheckingErrorStatisticsService healthCheckingErrorStatisticsService;
    @Autowired
    private AlertingDao alertingDao;
    @Autowired
    private AlertHistoryDao alertHistoryDao;
    @Autowired
    private AlertNoticeDao alertNoticeDao;
    @Autowired
    private AlertUserRouteServiceDao alertUserRouteServiceDao;
    @Autowired
    private AlertNoticeTask alertNoticeTask;

    private static final Logger LOGGER = LoggerFactory.getLogger(AlertDetectTask.class);

    //检测应用
    private DynamicStringProperty applicationNames = DynamicPropertyFactory.getInstance().getStringProperty("alert.applicationNames", "");
    //多少时间内的统计有效
    private DynamicIntProperty errorStatisticsTimeIntervalSeconds = DynamicPropertyFactory.getInstance().getIntProperty("alert.errorStatisticsTimeIntervalSeconds", 180);
    //出现多少错误触发报警
    private DynamicIntProperty errorStatisticsCount = DynamicPropertyFactory.getInstance().getIntProperty("alert.errorStatisticsCount", 5);
    private DynamicIntProperty noticeTotal = DynamicPropertyFactory.getInstance().getIntProperty("alert.noticeTotal", 5);

    @Scheduled(cron = "0/6 * * * * ?")
    public void task() {

        this.doSchedule();

    }

    private void doSchedule() {
        if (!Detect.notEmpty(applicationNames.get())) {
            return;
        }

        String[] applicationNameParts = applicationNames.get().split(",");
        for (String applicationNamePart : applicationNameParts) {
            this.dealServiceGateway(applicationNamePart.trim());
        }
    }

    private void dealServiceGateway(String applicationName) {
        HealthCheckingErrorStatistics record = new HealthCheckingErrorStatistics();
        record.setApplicationName(applicationName);

        List<HealthCheckingErrorStatistics> healthCheckingErrorStatistics = healthCheckingErrorStatisticsService.getListByRecord(record, null);
        if (!Detect.notEmpty(healthCheckingErrorStatistics)) {
            return;
        }

        healthCheckingErrorStatistics.forEach(e -> {
            this.dealErrorStatistics(e);
        });
    }

    private void dealErrorStatistics(HealthCheckingErrorStatistics healthCheckingErrorStatistics) {
        DateTime lastUpdateTime = new DateTime(healthCheckingErrorStatistics.getUpdateTime())
                .plusSeconds(errorStatisticsTimeIntervalSeconds.get());

        if (lastUpdateTime.isAfterNow()) {//检查区间内
            if (healthCheckingErrorStatistics.getCount() >= errorStatisticsCount.get()) {//够错误数后执行
                this.dealEffective(healthCheckingErrorStatistics);
            }
        } else {//检查区间外
            this.dealInvalid(healthCheckingErrorStatistics);
        }
    }

    private void dealInvalid(HealthCheckingErrorStatistics healthCheckingErrorStatistics) {
        healthCheckingErrorStatistics.setCount(0);//统计区间外重置统计次数
        healthCheckingErrorStatistics.setUpdateTime(new Date());

        healthCheckingErrorStatisticsService.updateByPrimaryKeySelective(healthCheckingErrorStatistics, null);
    }

    private void dealEffective(HealthCheckingErrorStatistics healthCheckingErrorStatistics) {
        RouteServiceUrl routeServiceUrl = new RouteServiceUrl();
        routeServiceUrl.setServiceId(healthCheckingErrorStatistics.getServiceId());
        routeServiceUrl.setUrl(healthCheckingErrorStatistics.getUrl());
        routeServiceUrl.setStatus((int) StatusEnum4RouteServiceUrl.EFFECTIVE.getCode());
        routeServiceUrl = routeServiceUrlService.getOneByRecord(routeServiceUrl, null);

        if (routeServiceUrl != null) {
            RouteServiceUrl record = new RouteServiceUrl();
            record.setId(routeServiceUrl.getId());
            record.setStatus((int) StatusEnum4RouteServiceUrl.ILL_HEALTH.getCode());
            record.setUpdateTime(new Date());

            routeServiceUrlService.updateByPrimaryKeySelective(record, null);//更新为不健康状态
        }


        AlertUserRouteService alertUserRouteServiceRecord = new AlertUserRouteService();
        alertUserRouteServiceRecord.setApplicationName(healthCheckingErrorStatistics.getApplicationName());
        alertUserRouteServiceRecord.setServiceId(healthCheckingErrorStatistics.getServiceId());

        List<AlertUserRouteService> dbAlertUserRouteServices = alertUserRouteServiceDao.getListByRecord(alertUserRouteServiceRecord);
        if (!Detect.notEmpty(dbAlertUserRouteServices)) {
            return;
        }

        Alerting alertingRecord = new Alerting();
        alertingRecord.setApplicationName(healthCheckingErrorStatistics.getApplicationName());
        alertingRecord.setServiceId(healthCheckingErrorStatistics.getServiceId());
        alertingRecord.setUrl(healthCheckingErrorStatistics.getUrl());

        if (alertingDao.getOneByRecord(alertingRecord) == null) {//告警不存在即添加
            alertingRecord.setCreateTime(new Date());

            AlertHistory alertHistory = new AlertHistory();
            BeanUtils.copyProperties(alertingRecord, alertHistory);

            alertingDao.saveSelective(alertingRecord);//新建告警

            alertHistory.setAlertingId(alertingRecord.getId());
            alertHistoryDao.saveSelective(alertHistory);//告警历史

            dbAlertUserRouteServices.forEach(e -> {
                AlertNotice alertNotice = new AlertNotice();
                alertNotice.setAlertingId(alertingRecord.getId());
                alertNotice.setAlertUserId(e.getAlterUserId());
                alertNotice.setNoticeTotal(noticeTotal.get());
                alertNotice.setNoticeCount(0);
                alertNotice.setStatus((int) StatusEnum4AlertNotice.EFFECTIVE.getCode());
                alertNotice.setCreateTime(new Date());
                alertNotice.setUpdateTime(new Date());

                alertNoticeDao.saveSelective(alertNotice);
            });

            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        CurAlertingSupportHolder.set(alertingRecord.getId());
                        alertNoticeTask.task();
                    } finally {
                        CurAlertingSupportHolder.reset();
                    }
                }
            }).start();//自动触发告警通知

        } else {
            //已有告警存在就忽略
        }
    }

}
